--[[
Wrapper module for MOAI Box2d operations. It provides functions for common tasks such as creating 
a Box2d world or creating bodies from json files output by Physics Body Editor.
--]]
module(..., package.seeall)

world = false  -- module-level reference to the box2d world

function create_world(gx, gy, units2meters)
    assert(world == false, "Box2d world already exists")
    world = MOAIBox2DWorld.new()
    world:setGravity(gx or 0, gy or -9.8)
    world:setUnitsToMeters(units2meters or 1)
    return world
end

function read_json(filepath, ...)
    --[[ Reads a json file created by Physics body editor and creates bodies from the data. 
    Returns a list of bodies created from the given file. ]]
    local f = assert(io.open(filepath, "r"))
    local t = MOAIJsonParser.decode(f:read("*a"))
    f:close()
    return build_bodies(t, ...)
end

function build_bodies(t, body_type)
    local bodies = {}
    for _, b in pairs(t.rigidBodies) do table.insert(bodies, build_body(b, body_type)) end
    return bodies
end

function build_body(t, body_type, x, y)
    local body = world:addBody(body_type, x, y)
    body.name = t.name
    body.image_path = t.imagePath
    body.origin = t.origin
    body.fixtures = {}
    for _, c in pairs(t.circles) do table.insert(body.fixtures, build_circle(body, c)) end
    for _, p in pairs(t.polygons) do table.insert(body.fixtures, build_polygon(body, p)) end
    body:resetMassData()
    return body
end

function build_circle(body, circle)
    return body:addCircle(circle.cx, circle.cy, circle.r)
end

function build_polygon(body, points)
    local coords = {}
    for _, point in ipairs(points) do
        table.insert(coords, point.x)
        table.insert(coords, point.y)
    end
    return body:addPolygon(coords)
end

function fixture_broadcast(fnc_name)
    --[[ Broadcasts a given function (specified by name) to all fixtures in a body. This is useful 
    to set common properties on all fixtures of a given body. ]]
    return function(body, ...)
        for _, fixture in ipairs(body.fixtures) do
            fixture[fnc_name](fixture, ...)
        end
    end
end

set_body = {collision_handler = fixture_broadcast("setCollisionHandler"),
            density = fixture_broadcast("setDensity"),
            filter = fixture_broadcast("setFilter"),
            friction = fixture_broadcast("setFriction"),
            restitution = fixture_broadcast("setRestitution")}
